import _wcsgrid
import numpy as np
import warnings

try:
    from coords import coord_conv
except ImportError:
    warnings.warn("Importing coord.coord_conv failed.")
    coord_conv = None


def tolatex(s):
    return s


def collect_ticks(pgptxt_textlist, x1, y1, x2, y2):

    ticks_left = []
    ticks_right = []
    ticks_top = []
    ticks_bottom = []

    ticklabels_left = []
    ticklabels_right = []
    ticklabels_top = []
    ticklabels_bottom = []

    for t in pgptxt_textlist:

        if t[1] <= y1:
            ticks_bottom.append(t[0])
            ticklabels_bottom.append(tolatex(t[-1].split()[0]))

        if t[1] >= y2:
            ticks_top.append(t[0])
            ticklabels_top.append(tolatex(t[-1].split()[0]))

        #if t[0] <= -0.5:
        if t[0] <= x1:
            ticks_left.append(t[1])
            ticklabels_left.append(tolatex(t[-1].split()[0]))

        #if t[0] >= naxis2-.5:
        if t[0] >= x2:
            ticks_right.append(t[1])
            ticklabels_right.append(tolatex(t[-1].split()[0]))

    assert len(pgptxt_textlist) <= len(ticks_bottom + ticks_left + ticks_top + ticks_right)
                
    return dict(ticks_left = ticks_left,
                ticks_right = ticks_right,
                ticks_top = ticks_top, 
                ticks_bottom = ticks_bottom, 
                ticklabels_left = ticklabels_left, 
                ticklabels_right = ticklabels_right, 
                ticklabels_top = ticklabels_top, 
                ticklabels_bottom = ticklabels_bottom)



    

def get_interpolated_point(x1, y1, x2, y2, d):
    d12 = ((x2-x1)**2 + (y2-y1)**2)**.5
    dx = x1+(x2-x1)*d/d12
    dy = y1+(y2-y1)*d/d12
    return dx, dy


def get_ticklines(lx, ly, x1, y1, x2, y2, ticklength):

    d = ((lx[1:] - lx[:-1])**2 + (ly[1:] - ly[:-1])**2)**.5
    da = np.add.accumulate(d)

    # if line is too short, return line itself
    if da[-1] < 2*ticklength:
        return [(lx, ly)]

    r = []
    
    # tick at line begin
    if (x1 < lx[0] < x2) and (y1 < ly[0] < y2):
        pass
    else:
        i = da.searchsorted(ticklength) + 1

        xx, yy = get_interpolated_point(lx[i], ly[i], lx[i+1], ly[i+1],
                                        ticklength - da[i-1])

        tick1x = np.concatenate([lx[:i], [xx]])
        tick1y = np.concatenate([ly[:i], [yy]])

        r.append((tick1x, tick1y))
        

    # tick at line end
    if (x1 < lx[-1] < x2) and (y1 < ly[-1] < y2):
        pass
    else:
        ticklength2 = da[-1] - ticklength
        i = da.searchsorted(ticklength2)

        xx, yy = get_interpolated_point(lx[i], ly[i], lx[i+1], ly[i+1],
                                        ticklength2 - da[i-1])

        tick2x = np.concatenate([[xx], lx[i+1:]])
        tick2y = np.concatenate([[yy], ly[i+1:]])

        r.append((tick2x, tick2y))
        


    return r


def test_ticklines():
    lx = np.arange(0., 15.1, 0.5)
    ly = np.array([1.]*len(lx))

    l = get_ticklines(lx, ly, 0, 0, 20, 20, ticklength=.6)
    #l = get_ticklines(lx, ly, ticklength=.75)





class grid(object):
    def __init__(self, wcs, x1, y1, x2, y2,
                 src_system=None, dest_system=None, 
                 coord_format="GE",
                 label_density=(8, 8),
                 grid1=None, grid2=None,
                 ):
        """
        coord_format    C(2)*(*) Formatting control for the world coordinates,
                        used for axis labelling (see notes 1 and 2):
                           ' ': plain numeric
                           'A': angle in degrees expressed in
                                decimal notation normalized in the
                                range [0,360).
                           'B': as 'A' but normalized in the range
                                (-180,180].
                           'C': as 'A' but unnormalized.
                           'D': angle in degrees expressed in
                                sexagesimal notation, DD^MM'SS\".S,
                                normalized in the range [0,360).
                           'E': as 'D' but normalized in the range
                                (-180,180].
                           'F': as 'D' but unnormalized.
                           'G': angle in degrees expressed in
                                hms notation, HHhMMmSSs.S, normalized
                                in the range [0,24) hours.
                           'H': as 'G' but normalized in the range
                                (-12,12] hours.
                           'I': as 'G' but unnormalized.
                           'L': logarithmic (see note 2)
                           'T': time in hours expressed as HH:MM:SS.S
                           'Y': Modified Julian Date to be expressed as
                                a Gregorian calendar date, YYYY/MM/DD.
                                (MJD = JD - 2400000.5.)

                        For the angular types, NLFUNC is assumed to
                        return the angle in degrees whereupon it will
                        be formatted for display in the specified way.
                        For example, an angle of -417.2958 degrees
                        (returned by NLFUNC):

                            'A':  302^.7042
                            'B':  -57^.2958
                            'C': -417^.2958
                            'D':  302^42'15\"
                            'E':  -57^17'45\"
                            'F': -417^17'45\"
                            'H':   20h10m49s
                            'I':   -3h49m11s
                            'J':  -27h49m11s





        labctl = (bottom, left, top, right)
           default = (1, 2, 1, 2)

    label_density : approximate number of grid number for each axis
                 default = (8, 8)
           
        """

        #if x2 is None:
        #    x1, x2 = -0.5, x1-0.5
        #if y2 is None:
        #    y1, y2 = -0.5, y1-0.5


        if grid1 is None and grid2 is None:
            grid1 = []
            grid2 = []
            # otherwise, if either one of grid1 or grid2 is None, they
            # not called with _wcsgrd.grid

        aux_coord_conv = []
        #if src_system is None and dest_system is None:
        #    aux_coord_conv = False
        #elif coord_conv is None:
        #    aux_coord_conv = False
        if (coord_conv is not None) and (dest_system is not None):
            if src_system is None:
                src_system = _guess(wcs)
                if not src_system:
                    raise Exception("")

            cf1 = coord_conv(src_system, dest_system)
            cf2 = cf1.inverse_conv()
            aux_coord_conv = [cf1, cf2]



        # tolerance for tick and label collecting.
        self._tol = min(x2-x1, y2-y1)/1000.

        #x1, y1 = -0.5, -0.5
        #x2, y2 = naxis1-0.5, naxis2-0.5
        
        axis1 = {}
        axis2 = {}
        
        #label_control=(1, 2, 1, 2),
        #labctl = sum(i1*i2 for i1, i2 in zip(label_control, [1, 10, 100, 1000]))
        #print labctl

        assert (len(coord_format) == 2)

        assert (len(label_density) == 2)
        labden = label_density[0] + 100*label_density[1]


        # AXIS 1
        
        _wcsgrid.wcsgrid_pgline_linelist = []
        _wcsgrid.wcsgrid_pgptxt_textlist = []


        labctl = 1111

        if not grid1 is None:
            if aux_coord_conv:
                _wcsgrid.wcsgrid_coord_conv(wcs, x1, y1, x2, y2,
                                            coord_format, labctl, labden,
                                            1, grid1,
                                            aux_coord_conv[0], aux_coord_conv[1])
            else:
                _wcsgrid.wcsgrid(wcs, x1, y1, x2, y2,
                                 coord_format, labctl, labden,
                                 1, grid1)
        
        d = self._tol
        axis1["grid_lines"] = _wcsgrid.wcsgrid_pgline_linelist
        axis1.update(collect_ticks(_wcsgrid.wcsgrid_pgptxt_textlist,
                                   x1+d, y1+d, x2-d, y2-d))


        axis1["_data"] = ( _wcsgrid.wcsgrid_pgline_linelist,
                           _wcsgrid.wcsgrid_pgptxt_textlist)
        


        # AXIS 2

        _wcsgrid.wcsgrid_pgline_linelist = []
        _wcsgrid.wcsgrid_pgptxt_textlist = []


        labctl = 2222

        if not grid2 is None:
            if aux_coord_conv:
                _wcsgrid.wcsgrid_coord_conv(wcs, x1, y1, x2, y2,
                                            coord_format, labctl, labden,
                                            2, grid2,
                                            aux_coord_conv[0], aux_coord_conv[1])
            else:
                _wcsgrid.wcsgrid(wcs, x1, y1, x2, y2,
                                 coord_format, labctl, labden,
                                 2, grid1)


        axis2["grid_lines"] = _wcsgrid.wcsgrid_pgline_linelist
        axis2.update(collect_ticks(_wcsgrid.wcsgrid_pgptxt_textlist,
                                   x1+d, y1+d, x2-d, y2-d))

        axis2["_data"] = ( _wcsgrid.wcsgrid_pgline_linelist,
                           _wcsgrid.wcsgrid_pgptxt_textlist)


        #print y_ticks

        #self.naxis1 = naxis1
        #self.naxis2 = naxis2
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        
        self.axis1 = axis1
        self.axis2 = axis2
        
        self.grid_lines = axis1["grid_lines"] + axis2["grid_lines"]

        self.x_ticks = axis1["ticks_bottom"]
        self.x_ticklabels = axis1["ticklabels_bottom"]

        self.y_ticks=y_ticks = axis2["ticks_left"]
        self.y_ticklabels = axis2["ticklabels_left"]

                
    def get_ticklines(self, ticklength):

        #t2 = ticklength**2

        d = self._tol

        ticklines1 = []
        for lx, ly in self.axis1["grid_lines"]:
            ticklines1.extend(get_ticklines(lx, ly,
                                            self.x1+d, self.y1+d, self.x2-d, self.y2-d,
                                            ticklength))

        ticklines2 = []
        for lx, ly in self.axis2["grid_lines"]:
            ticklines2.extend(get_ticklines(lx, ly,
                                            self.x1+d, self.y1+d, self.x2-d, self.y2-d,
                                            ticklength))

        return ticklines1, ticklines2



grid_with_coord_conv = grid
